﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace MassIntellect.Drawing
{
	public abstract partial class Shape : ICloneable, INotifyPropertyChanged
	{
		public static readonly Pen DefaultPen = Pens.Black;
		public static readonly Brush DefaultBrush = Brushes.Gold;

		#region Свойства

		private int _Opacity;
		/// <summary>
		/// 0..100
		/// </summary>
		public virtual float Opacity
		{
			get
			{
				return ((float)_Opacity / 255f) * 100f;
			}
			set
			{
				var op = Math.Max(0, Math.Min(255, (int)((value / 100) * 255)));
				if (op != _Opacity)
				{
					_Opacity = op;
					OnPropertyChanged("Opacity");
				}
			}
		}

		private Pen _Pen;
		public virtual Pen Pen
		{
			get
			{ return _Pen; }
			set
			{
				if (value != _Pen)
				{
					_Pen = value;
					OnPropertyChanged("Pen");
				}
			}
		}

		private Brush _Brush;
		public virtual Brush Brush
		{
			get
			{ return _Brush; }
			set
			{
				if (value != _Brush)
				{
					_Brush = value;
					OnPropertyChanged("Brush");
				}
			}
		}

		private float _X;
		public virtual float X
		{
			get
			{ return _X; }
			set
			{
				if (value != _X)
				{
					_X = value;
					OnPropertyChanged("X");
					OnPropertyChanged("Right");
				}
			}
		}

		private float _Y;
		public virtual float Y
		{
			get
			{ return _Y; }
			set
			{
				if (value != _Y)
				{
					_Y = value;
					OnPropertyChanged("Y");
					OnPropertyChanged("Bottom");
				}
			}
		}

		private float _Width;
		public virtual float Width
		{
			get
			{ return _Width; }
			set
			{
				if (value != _Width)
				{
					_Width = value;
					OnPropertyChanged("Width");
					OnPropertyChanged("Right");
				}
			}
		}

		private float _Height;
		public virtual float Height
		{
			get
			{ return _Height; }
			set
			{
				if (value != _Height)
				{
					_Height = value;
					OnPropertyChanged("Height");
					OnPropertyChanged("Bottom");
				}
			}
		}

		private Color _Color;
		public virtual Color Color
		{
			get
			{ return _Color; }
			set
			{
				if (value != _Color)
				{
					_Color = value;
					OnPropertyChanged("Color");
				}
			}
		}

		public float Right
		{
			get
			{
				return X + Width;
			}
		}

		public float Bottom
		{
			get
			{
				return Y + Height;
			}
		}

		public virtual RectangleF Bounds
		{
			get
			{
				return new RectangleF(X, Y, Width, Height);
			}
		}

		private bool _IsOutlining = true;
		/// <summary>
		/// Обведен
		/// </summary>
		public virtual bool IsOutlining
		{
			get
			{ return _IsOutlining; }
			set
			{
				if (value != _IsOutlining)
				{
					_IsOutlining = value;
					OnPropertyChanged("IsOutlining");
				}
			}
		}

		private bool _IsDrawingOwner;
		/// <summary>
		/// Прорисовывается владельцем
		/// </summary>
		public virtual bool IsDrawingOwner
		{
			get
			{ return _IsDrawingOwner; }
			set
			{
				if (value != _IsDrawingOwner)
				{
					_IsDrawingOwner = value;
					OnPropertyChanged("IsDrawingOwner");
				}
			}
		}

		private bool _IsFilled;
		/// <summary>
		/// Залит
		/// </summary>
		public virtual bool IsFilled
		{
			get
			{ return _IsFilled; }
			set
			{
				if (value != _IsFilled)
				{
					_IsFilled = value;
					OnPropertyChanged("IsFilled");
				}
			}
		}


		private bool _IsVisible = true;
		public virtual bool IsVisible
		{
			get
			{ return _IsVisible; }
			set
			{
				if (value != _IsVisible)
				{
					_IsVisible = value;
					OnPropertyChanged("IsVisible");
				}
			}
		}

		/// <summary>
		/// Стек из свойств Видимость, Залитость, Контур
		/// </summary>
		public bool IsFulled
		{
			get
			{
				return IsFilled && IsOutlining && IsVisible;
			}
			set
			{
				IsFilled = IsOutlining = IsVisible = value;
			}
		}

		public abstract GraphicsPath GraphicsPath { get; }

		#endregion

		#region Методы

		public virtual void DrawToGraphics(Graphics gr)
		{
			if (IsVisible)
			{
				if (!IsDrawingOwner)
				{
					GraphicsPath grp = GraphicsPath;
					if (IsFilled)
						gr.FillPath(Brush ?? DefaultBrush, grp);
					if (IsOutlining)
						gr.DrawPath(Pen ?? DefaultPen, grp);
				}
			}
		}

		public abstract bool Contains(PointF pnt);

		/// <summary>
		/// Точка пересечения (ближайшая) с прямой, проходящей через центр фигуры и указанную точку).
		/// По-умолчанию береться ограничивающий прямоугольник.
		/// </summary>
		/// <param name="pnt"></param>
		/// <returns></returns>
		public virtual PointF Joint(PointF pnt)
		{
			PointF Res, Center = new PointF(X + Width / 2, Y + Height / 2);
			if (!JointVectors(X, Y, Right, Y, Center.X, Center.Y, pnt.X, pnt.Y, out Res)
			    && !JointVectors(X, Y, X, Bottom, Center.X, Center.Y, pnt.X, pnt.Y, out Res)
			    && !JointVectors(X, Bottom, Right, Bottom, Center.X, Center.Y, pnt.X, pnt.Y, out Res)
			    && !JointVectors(Right, Y, Right, Bottom, Center.X, Center.Y, pnt.X, pnt.Y, out Res))
				Res.X = Res.Y = float.NaN;
			return Res;
		}

		#endregion

		#region ICloneable Members

		protected T Cloning<T>()
			where T : Shape, new()
		{
			T sh = new T();
			sh.Opacity = Opacity;
			sh.Pen = Pen;
			sh.Brush = Brush;
			sh.X = X;
			sh.Y = Y;
			sh.Width = Width;
			sh.Height = Height;
			sh.Color = Color;
			sh.IsFilled = IsFilled;
			sh.IsOutlining = IsOutlining;
			sh.IsVisible = IsVisible;
			return sh;
		}

		public abstract object Clone();

		#endregion

		#region INotifyPropertyChanged Members

		protected void OnPropertyChanged(string propName)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propName));
		}

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion
	}
}